package com.wolfeyes.common.utils.redis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
//import java.util.concurrent.ConcurrentMap;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wolfeyes.common.utils.serialize.SerializeUtils;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

/**
 * @description 定义redis缓存实现类RedisCache
 * @projectName bootdo
 * @className RedisCache.java
 * @author yanyljava
 * @email yanyljava@qq.com
 * @date 2018-07-11 15:53:58
 * @since 2018年07月12日上午11:37:39
 * @createTime 2018年06月09日 下午17:04:13
 * @version 1.0.0
 */
@Data
@NoArgsConstructor
@RequiredArgsConstructor
@AllArgsConstructor
public class RedisCache<K, V> implements Cache<K, V> {

	/**
	 * slf4j日志对象
	 */
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * The wrapped Jedis instance.
     * 浅译：已包装好的自定义的redis底层实现类redisManager对象实例的引用
     */
    @NonNull
    private RedisManager redisManager;

    /**
     * The Redis key prefix for the sessions
     * 浅译：默认值是会话session的密钥key前缀
     */
    @Getter
    @Setter
    private String keyPrefix = "shiro_redis_session:";

	/**
	 * 使用lombok 插件 注解 @Data @Getter @Setter
	 * 参考博客：https://blog.csdn.net/icecoola_/article/details/77414572
	 * 参考博客：https://blog.csdn.net/cdyjy_litao/article/details/53759928
	 * 参考博客：https://blog.csdn.net/dorothy1224/article/details/79280591/
	 * 参考博客：http://www.blogjava.net/fancydeepin/archive/2012/07/12/lombok.html
	 */
    
    /*
    /**
     * Returns the Redis session keys
     * keyPrefix.
     * @return The keyPrefix
     * /
    public String getKeyPrefix() {
        return keyPrefix;
    }

    /**
     * Sets the Redis sessions key
     * keyPrefix.
     * @param keyPrefix The prefix
     * /
    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    /**
     * 有参构造方法
     * @description 通过一个JedisManager实例构造RedisCache
     * @param redisManager 自定义的redis底层实现类
     * /
    public RedisCache(RedisManager redisManager) {
    	
    	// 非空判断
        if (redisManager == null) {
        	
            throw new IllegalArgumentException("Cache argument cannot be null.");
        }
        
        this.redisManager = redisManager;
    }

    /**
     * Constructs a cache instance with the specified
     * Redis manager and using a custom key prefix.
     * 浅译：构造一个特定的缓存实例。
     * 浅译：Redis manager使用自定义密钥key前缀。
     * @param cache The cache manager instance redis底层实现类redisManager的对象实例
     * @param prefix The Redis key prefix 自定义密钥key前缀
     * /
	public RedisCache(RedisManager redisManager, String keyPrefix) {
		
		// 有参构造方法
		this(redisManager);

        // set the keyPrefix
        this.keyPrefix = keyPrefix;
    }
    */

    /**
     * @title get Byte Key from key(convert key to byte[])
     * @description 根据属性key，获得byte[]字节数组类型的key
     * @param key 属性key
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return byte[] 属性key的byte[]字节数组
     * @throws Exception 异常
     */
    private byte[] getByteKey(K key) {
    	
        if(key instanceof String){
        	
        	// 拼接密钥key前缀
            String preKey = this.keyPrefix + key;
            
            // 获取拼接密钥key前缀后的byte[]字节数组
            byte[] preKeyBytes = preKey.getBytes();
            
            return preKeyBytes;
        }
        
    	// 序列化，获得属性key的byte[]字节数组
    	byte[] keyBytes = SerializeUtils.serialize(key);
    	
		return keyBytes;
    }

    /**
     * @title get value from redis
     * @description 根据属性key，从Redis缓存中，快速获得属性值value
     * @param key 属性key
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return byte[] 属性值value
     * @throws Exception 异常
     */
    @Override
    public V get(K key) throws CacheException {
    	
        this.logger.debug("根据key从Redis中获取对象 key [" + key + "]");
        
        try {
        	
        	// 判断非空
            if (key == null) {
            	
                return null;
            }
            
            // 根据属性key，获得byte[]字节数组类型的key
            byte[] preKeyBytes = this.getByteKey(key);
            
            // 从Redis缓存中，快速获取属性值value的字节数组
			byte[] valueBytes = this.redisManager.get(preKeyBytes);
			
			@SuppressWarnings("unchecked")
			// 反序列化，获得属性值value
			V value = (V) SerializeUtils.deserialize(valueBytes);
			
			// 返回属性值value
			return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        }

    }

    /**
     * @title set key and value to redis
     * @description 根据属性key，属性值value；存储至redis缓存
     * @param key 属性key
     * @param value 属性值value
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return Value 属性值value
     * @throws Exception 异常
     */
    @Override
    public V put(K key, V value) throws CacheException {
    	
        this.logger.debug("根据key从Redis中存储对象 key [{}]", key);
        
        try {
        	
        	// 根据属性key，获得byte[]字节数组类型的key
        	byte[] preKeyBytes = this.getByteKey(key);
        	
        	// 序列化，获得属性值value的byte[]字节数组
        	byte[] valueBytes = SerializeUtils.serialize(value);
        	
        	// 根据属性key的字节数组，属性值value的字节数组；存储至redis缓存
        	this.redisManager.set(preKeyBytes, valueBytes);
        	
        	// 返回属性值value
            return value;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * @title del key-value from redis
     * @description 根据属性key，从redis缓存中删除此键值对：key-value
     * @param key 属性key
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return Value 历史属性值previousValue
     * @throws Exception 异常
     */
    @Override
    public V remove(K key) throws CacheException {
    	
        this.logger.debug("根据key从redis中删除对象 key [{}]", key);
        
        try {
        	
        	// 根据属性key，获得byte[]字节数组类型的key
        	byte[] preKeyBytes = this.getByteKey(key);
        	
        	// 根据属性key，从Redis缓存中，快速获得属性值value
            V previousValue = this.get(key);
            
            this.redisManager.del(preKeyBytes);
            
            // 历史属性值previousValue
            return previousValue;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * @title exist key from redis
     * @description 判断redis缓存中是否存在此key值
     * @param key 属性key
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return boolean result 是否存在此key值：true/存在，false/不存在
     * @throws Exception 异常
     */
    public boolean exist(K key) {
    	
    	this.logger.debug("根据key从redis中判断此对象 key [{}]是否存在", key);
    	
    	boolean result = false;
    	
    	String keyString = null;
    	
    	// 判断属性key类型
    	if(key instanceof String) {
    		
    		// 强制类型转换
    		keyString = (String) key;
    		
    	}
    	
    	// java.lang.Object.toString()
    	keyString = key.toString();
    	
    	// 判断redis缓存中是否存在此key值
    	result = this.redisManager.exist(keyString);
    	
    	return result;
    }
    
    /**
     * @title clear from redis
     * @description 清空redis缓存-clear
     * @param 
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return void
     * @throws Exception 异常
     */
    @Override
    public void clear() throws CacheException {
    	
        this.logger.debug("从redis中删除所有元素");
        
        try {
        	
        	// 清空redis缓存-flushDB
        	this.redisManager.flushDB();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * @title size from redis
     * @description 获取redis缓存大小-size
     * @param 
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return void
     * @throws Exception 异常
     */
    @Override
    public int size() {
    	
    	this.logger.debug("获取redis缓存大小-size");
    	
        try {
        	
        	// 获取redis缓存大小-dbSize
        	Long dbSize = this.redisManager.dbSize();
        	
        	return dbSize.intValue();
        	
        	// 装箱操作-Long
            // Long longSize = new Long(dbSize);
            
            // return longSize.intValue();
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * @title keys from redis
     * @description 获取redis缓存中所有键-keys
     * @param pattern regex正则表达式
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return Set<byte[]> keys 所有键-keys的Set集合
     * @throws Exception 异常
     */
    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keys() {
    	
    	this.logger.debug("从redis缓存中获取所有键-keys");
    	
        try {
        	
        	// 获取redis缓存中所有键-keys
            Set<byte[]> keys = this.redisManager.keys(this.keyPrefix + "*");
            
            // 判断非空
            if (CollectionUtils.isEmpty(keys)) {
            	
            	// 返回空的 set（不可变的）
                return Collections.emptySet();
                
            }
            
			// 创建新的keys的Set集合-newKeys
			Set<K> newKeys = new HashSet<>();
			
			// 遍历keys的Set集合
			for (byte[] key : keys) {
				
				// 强制类型转换(byte[] --> K)
				newKeys.add((K) key);
			}
			
			// 返回新的keys的Set集合-newKeys
			return newKeys;
        } catch (Throwable t) {
            throw new CacheException(t);
        }
    }

    /**
     * @title values from redis
     * @description 获取redis缓存中所有属性值-values
     * @param pattern regex正则表达式
     * @author yanyl
     * @since 2018年10月08日上午13:37:39
     * @return Set<byte[]> keys 所有属性值-values的Set集合
     * @throws Exception 异常
     */
    @Override
    public Collection<V> values() {
    	
    	this.logger.debug("从redis缓存中获取所有属性值-values");
    	
        try {
        	
        	// 获取redis缓存中所有键-keys
            Set<byte[]> keys = this.redisManager.keys(this.keyPrefix + "*");
            
            // 判断非空
            if (!CollectionUtils.isEmpty(keys)) {
            	
            	// 指定List集合初始容量大小
            	int initialCapacity = keys.size();
            	
            	// 创建新的values的List集合-valueList
                List<V> valueList = new ArrayList<>(initialCapacity);
                
                // 遍历keys的Set集合
                for (byte[] key : keys) {
                	
                	// 强制类型转换(byte[] --> K)
                	// 根据属性key，从Redis缓存中，快速获得属性值value
                    @SuppressWarnings("unchecked")
                    V value = this.get((K)key);
                    
                    // 判断非空
                    if (value != null) {
                    	
                    	// 添加到属性值values的List集合-valueList
                    	valueList.add(value);
                    }
                }
                
                // 返回指定列表的不可修改视图
                return Collections.unmodifiableList(valueList);
            }
            
            // 返回空的列表（不可变的）
			return Collections.emptyList();
			
        } catch (Throwable t) {
        	
            throw new CacheException(t);
        }
    }

}

